#pragma once

/* SPDX-License-Identifier: GPL-2.0 */
/*
 * workqueue.h --- work queue handling for Linux.
 */

#include <linux/workqueue_types.h>
#include <linux/threads.h>

enum work_bits
{
    WORK_STRUCT_PENDING_BIT = 0, /* work item is pending execution */
    WORK_STRUCT_INACTIVE_BIT,    /* work item is inactive */
    WORK_STRUCT_PWQ_BIT,         /* data points to pwq */
    WORK_STRUCT_LINKED_BIT,      /* next work is linked to this one */
#ifdef CONFIG_DEBUG_OBJECTS_WORK
    WORK_STRUCT_STATIC_BIT, /* static initializer (debugobjects) */
#endif
    WORK_STRUCT_FLAG_BITS,

    /* color for workqueue flushing */
    WORK_STRUCT_COLOR_SHIFT = WORK_STRUCT_FLAG_BITS,
    WORK_STRUCT_COLOR_BITS = 4,

    /*
     * When WORK_STRUCT_PWQ is set, reserve 8 bits off of pwq pointer w/
     * debugobjects turned off. This makes pwqs aligned to 256 bytes (512
     * bytes w/ DEBUG_OBJECTS_WORK) and allows 16 workqueue flush colors.
     *
     * MSB
     * [ pwq pointer ] [ flush color ] [ STRUCT flags ]
     *                     4 bits        4 or 5 bits
     */
    WORK_STRUCT_PWQ_SHIFT = WORK_STRUCT_COLOR_SHIFT + WORK_STRUCT_COLOR_BITS,

    /*
     * data contains off-queue information when !WORK_STRUCT_PWQ.
     *
     * MSB
     * [ pool ID ] [ disable depth ] [ OFFQ flags ] [ STRUCT flags ]
     *                  16 bits          1 bit        4 or 5 bits
     */
    WORK_OFFQ_FLAG_SHIFT = WORK_STRUCT_FLAG_BITS,
    WORK_OFFQ_BH_BIT = WORK_OFFQ_FLAG_SHIFT,
    WORK_OFFQ_FLAG_END,
    WORK_OFFQ_FLAG_BITS = WORK_OFFQ_FLAG_END - WORK_OFFQ_FLAG_SHIFT,

    WORK_OFFQ_DISABLE_SHIFT = WORK_OFFQ_FLAG_SHIFT + WORK_OFFQ_FLAG_BITS,
    WORK_OFFQ_DISABLE_BITS = 16,
};

enum wq_misc_consts
{
    WORK_NR_COLORS = (1 << WORK_STRUCT_COLOR_BITS),

    /* not bound to any CPU, prefer the local CPU */
    WORK_CPU_UNBOUND = NR_CPUS,

    /* bit mask for work_busy() return values */
    WORK_BUSY_PENDING = 1 << 0,
    WORK_BUSY_RUNNING = 1 << 1,

    /* maximum string length for set_worker_desc() */
    WORKER_DESC_LEN = 32,
};

#define __WORK_INITIALIZER(n, f)           \
    {                                      \
        .entry = {&(n).entry, &(n).entry}, \
        .func = (f),                       \
    }

#define DECLARE_WORK(n, f) \
    struct work_struct n = __WORK_INITIALIZER(n, f)

struct workqueue_struct *alloc_workqueue_noprof(const char *fmt, unsigned int flags,
                                                int max_active, ...);

#define alloc_workqueue(...) alloc_workqueue_noprof(__VA_ARGS__)

extern bool queue_work_on(int cpu, struct workqueue_struct *wq,
                          struct work_struct *work);

void workqueue_init_early(void);

/**
 * queue_work - queue work on a workqueue
 * @wq: workqueue to use
 * @work: work to queue
 *
 * Returns %false if @work was already on a queue, %true otherwise.
 *
 * We queue the work to the CPU on which it was submitted, but if the CPU dies
 * it can be processed by another CPU.
 *
 * Memory-ordering properties:  If it returns %true, guarantees that all stores
 * preceding the call to queue_work() in the program order will be visible from
 * the CPU which will execute @work by the time such work executes, e.g.,
 *
 * { x is initially 0 }
 *
 *   CPU0				CPU1
 *
 *   WRITE_ONCE(x, 1);			[ @work is being executed ]
 *   r0 = queue_work(wq, work);		  r1 = READ_ONCE(x);
 *
 * Forbids: r0 == true && r1 == 0
 */
static inline bool queue_work(struct workqueue_struct *wq,
                              struct work_struct *work)
{
    return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}

/**
 * schedule_work - put work task in global workqueue
 * @work: job to be done
 *
 * Returns %false if @work was already on the kernel-global workqueue and
 * %true otherwise.
 *
 * This puts a job in the kernel-global workqueue if it was not already
 * queued and leaves it in the same position on the kernel-global
 * workqueue otherwise.
 *
 * Shares the same memory-ordering properties of queue_work(), cf. the
 * DocBook header of queue_work().
 */
bool schedule_work(struct work_struct *work);
